#include "vm/page.h"
#include <debug.h>
#include <inttypes.h>
#include <round.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <hash.h>
#include "filesys/file.h"
#include "threads/thread.h"
#include "threads/malloc.h"
#include "threads/pte.h"
#include "threads/interrupt.h"
#include "vm/frame.h"
#include "devices/block.h"

unsigned
page_hash (const struct hash_elem *p_, void *aux UNUSED)
{
  const struct page *p = hash_entry (p_, struct page, hash_elem);
  return hash_bytes (&p->addr, sizeof p->addr);
}

// 在比较函数中指定比较的方式是addr，在hash_init时传入page_less指定比较函数
bool
page_less (const struct hash_elem *a_, const struct hash_elem *b_,
           void *aux UNUSED)
{
  const struct page *a = hash_entry (a_, struct page, hash_elem);
  const struct page *b = hash_entry (b_, struct page, hash_elem);

  return a->addr < b->addr;
}

// 在page_table（pt）中寻找虚拟地址address对应的page，hash_find的比较方式在hash_init（start_process）中指定为page_less
struct page *
page_lookup (struct hash *pt, const void *address)
{
  struct page p;
  struct hash_elem *e;

  p.addr = pg_round_down (address);
  e = hash_find (pt, &p.hash_elem);
  return e != NULL ? hash_entry (e, struct page, hash_elem) : NULL;
}

// 新建一个页
static bool
new_page (struct hash *pt, struct thread *thread, void *vaddr, bool writable,
          struct file *file, unsigned offset, unsigned length, int type)
{
  struct page *p = (struct page *) malloc (sizeof (struct page));
  p->addr = vaddr;
  p->data_type = type;
  p->thread = thread;
  p->wirtable = writable;
  p->sector = (block_sector_t) -1;

  p->file = file;
  p->length = length;
  p->offset = offset;

  lock_init (&p->page_lock);
 
  ASSERT (hash_insert (pt, &p->hash_elem) == NULL);
  
  //printf ("%p %d %p %d %d\n", vaddr, type, file, length, offset);

  return true;
}

// 新建一个存放file的page
bool
page_new_file (struct hash *pt, struct thread *thread, void *upage, bool writable,
               struct file *file, unsigned offset, unsigned length)
{
  return new_page (pt, thread, upage, writable, file, offset, length, 0);
}

// 新建一个存放stack的page，并放入page_table中
void
page_new_stack (struct hash *pt, struct thread *thread, void *vaddr)
{
  new_page (pt, thread, vaddr, true, NULL, 0, 0, 1);
}

// 新建一个mmap的page
bool 
page_new_mmap (struct hash *pt, struct thread *thread, void *vaddr,
               struct file *file, unsigned offset, unsigned length)
{
  return new_page (pt, thread, vaddr, true, file, offset, length, 2);
}

/* Can't be used in hash table iteration. */
// 释放该page
bool
page_free (struct page *page)
{
  frame_reload_page (page->addr);
  frame_evict_page (page->addr, false);
  hash_delete (&page->thread->page_table, &page->hash_elem);
  free (page);
  return true;
} 

static void
destroy_page (struct hash_elem *p_, void *aux UNUSED)
{
  struct page *p = hash_entry (p_, struct page, hash_elem);
  free (p);
}

void
page_table_destroy (struct hash *pt)
{
  if (pt != NULL)
    hash_destroy (pt, destroy_page);
}